<template>
    <div class="timeline-nuxt-container full-box" :style="containerStyle">
        <div class="timeline-nuxt" :class="[
            `timeline-${optionsSetup.direction || 'vertical'}`,
            `timeline-type-${optionsSetup.timelineType || 'default'}`
        ]" ref="timelineContent">
            <div v-for="(item, index) in timelineData" :key="index" class="timeline-item" :class="{
                'timeline-item-clickable': optionsSetup.clickable,
                'timeline-item-animated': optionsSetup.enableAnimation
            }" :style="itemStyle" @click="handleItemClick(item, index)">
                <!-- 时间线圆点 -->
                <div class="timeline-dot" :style="getDotStyle(item.type)"
                    :class="`timeline-dot-${item.type || 'primary'}`"> </div>

                <!-- 时间线连接线 -->
                <div v-if="index < timelineData.length - 1" class="timeline-line" :style="lineStyle"></div>

                <!-- 内容区域 -->
                <div class="timeline-content" :style="contentStyle">
                    <!-- 时间显示 -->
                    <div class="timeline-time" :style="timeStyle">
                        {{ item.time }}
                    </div>

                    <!-- 标题 -->
                    <div class="timeline-title" :style="titleStyle">
                        {{ item.title }}
                    </div>

                    <!-- 描述 -->
                    <div v-if="item.description" class="timeline-description" :style="descriptionStyle">
                        {{ item.description }}
                    </div>

                    <!-- 图片 -->
                    <div v-if="item.imageUrl" class="timeline-image-container" :style="imageContainerStyle">
                        <img :src="item.imageUrl" :alt="item.title" class="timeline-image" :style="imageStyle"
                            @click="handleImageClick(item, index)" />
                        <!-- 图片点击放大查看的提示 -->
                        <div v-if="optionsSetup.showImageZoomHint" class="timeline-image-hint">点击放大</div>
                    </div>

                </div>


            </div>
        </div>
    </div>
</template>

<script>
import { targetWidgetLinkageLogic, originWidgetLinkageLogic } from "@/views/bigscreenDesigner/designer/linkageLogic";

export default {
    name: 'widgetTimelineNuxt',
    props: {
        value: Object,
        ispreview: Boolean,
        widgetIndex: {
            type: Number,
            default: 0,
        },
    },
    data() {
        return {
            options: {},
            optionsSetup: {},
            optionsPosition: {},
            optionsData: {},
            timelineData: [],
            allComponentLinkage: [],
            scrollTimer: null,
            rafId: null,
            lastTimestamp: null,
            scrollPosition: 0,
            isScrolling: false,
            cycleTime: 60000, // 默认60秒循环时间
            // 用于无缝循环滚动时保存原始项目数量
            _originalItemCount: 0,
            // wrap 时可能使用的超时 id，用于清理
            _wrapTimeout: null,
            // 克隆前的原始内容高度（像素）
            _originalContentHeight: 0,
            // 图片加载事件处理器引用（用于移除监听）
            _imageLoadHandler: null,
        }
    },
    computed: {
        containerStyle() {
            const allStyle = this.optionsPosition;
            return {
                position: this.ispreview ? "absolute" : "static",
                width: allStyle.width + "px",
                height: allStyle.height + "px",
                left: allStyle.left + "px",
                top: allStyle.top + "px",
                overflow: 'hidden', // 隐藏滚动条
                // CSS变量
                '--dot-size': (this.optionsSetup.dotSize || 12) + 'px',
                '--line-color': this.optionsSetup.primaryColor || '#007bff',
                '--line-width': (this.optionsSetup.lineWidth || 2) + 'px'
            };
        },
        itemStyle() {
            const spacing = this.optionsSetup.itemSpacing || 24;
            return {
                marginBottom: this.optionsSetup.direction === 'horizontal' ? '0' : spacing + 'px',
                marginRight: this.optionsSetup.direction === 'horizontal' ? spacing + 'px' : '0',
                marginLeft: (this.optionsSetup.leftSpacing || 24) - (this.optionsSetup.dotSize || 12) + 'px',
                paddingLeft: (this.optionsSetup.leftSpacing || 24) + 'px',
            };
        },

        dotStyle() {
            const size = this.optionsSetup.dotSize || 12;
            const primaryColor = this.optionsSetup.primaryColor || '#007bff';

            // 根据是否空心调整样式
            if (this.optionsSetup.dotIsHollow) {
                return {
                    width: size + 'px',
                    height: size + 'px',
                    backgroundColor: 'transparent', // 空心时背景透明
                    border: `2px solid ${primaryColor}`,
                    display: 'flex',
                    alignItems: 'center',
                    justifyContent: 'center'
                };
            } else {
                // 优化：明确设置背景色和边框颜色相同
                const dotColor = primaryColor;
                return {
                    width: size + 'px',
                    height: size + 'px',
                    backgroundColor: dotColor,
                    border: `2px solid ${dotColor}`,
                    display: 'flex',
                    alignItems: 'center',
                    justifyContent: 'center'
                };
            }
        },

        dotInnerStyle() {
            const size = (this.optionsSetup.dotSize || 12) - 4;
            return {
                width: size + 'px',
                height: size + 'px'
            };
        },

        lineStyle() {
            const width = this.optionsSetup.lineWidth || 2;
            const primaryColor = this.optionsSetup.primaryColor || '#007bff';
            const dotSize = this.optionsSetup.dotSize || 12;

            if (this.optionsSetup.direction === 'vertical') {
                return {
                    width: width + 'px',
                    backgroundColor: primaryColor,
                    // 精确计算位置
                    left: `calc(${(dotSize / 2) - (width / 2)}px)`,
                    top: (dotSize + 12) + 'px', // 从圆点底部开始
                    bottom: '12px' // 到内容底部
                };
            } else {
                return {
                    height: width + 'px',
                    backgroundColor: primaryColor,
                    top: `calc(${(dotSize / 2) - (width / 2)}px)`,
                    left: '50%',
                    right: '-50%'
                };
            }
        },

        contentStyle() {
            return {
                padding: this.optionsSetup.timelineType === 'card' ? '16px' : '0',
                backgroundColor: this.optionsSetup.useCustomBgColor ? this.optionsSetup.contentBgColor : (this.optionsSetup.timelineType === 'card' ? '#ffffff' : 'transparent'),
                borderRadius: this.optionsSetup.timelineType === 'card' ? '8px' : '0',
                boxShadow: this.optionsSetup.timelineType === 'card' ? '0 2px 8px rgba(0,0,0,0.1)' : 'none',
                textAlign: this.optionsSetup.contentAlign || 'left',
                marginLeft: '0px'
            };
        },

        timeStyle() {
            return {
                color: this.optionsSetup.timeColor || '#6b7280',
                fontSize: (this.optionsSetup.timeFontSize || 14) + 'px',
                fontWeight: this.optionsSetup.timeFontWeight || 'normal',
                marginBottom: '4px'
            };
        },

        titleStyle() {
            return {
                color: this.optionsSetup.titleColor || '#1f2937',
                fontSize: (this.optionsSetup.titleFontSize || 16) + 'px',
                fontWeight: this.optionsSetup.titleFontWeight || '500',
                marginBottom: this.optionsSetup.timelineType === 'minimal' ? '0' : '8px'
            };
        },

        descriptionStyle() {
            return {
                color: this.optionsSetup.descriptionColor || '#6b7280',
                fontSize: (this.optionsSetup.descriptionFontSize || 14) + 'px',
                lineHeight: '1.5'
            };
        },
        // 图片容器样式
        imageContainerStyle() {
            const maxWidth = this.optionsSetup.imageMaxWidth || 200;
            const maxHeight = this.optionsSetup.imageMaxHeight || 150;
            return {
                marginTop: '8px',
                marginBottom: '4px',
                position: 'relative',
                display: 'inline-block',
                width: maxWidth + 'px',
                height: maxHeight + 'px',
                overflow: 'hidden'
            };
        },

        // 图片样式
        imageStyle() {
            const maxWidth = this.optionsSetup.imageMaxWidth || 200;
            const maxHeight = this.optionsSetup.imageMaxHeight || 150;
            const borderRadius = this.optionsSetup.imageBorderRadius || 4;
            const displayMode = this.optionsSetup.imageDisplayMode || 'contain';
            return {
                width: '100%',
                height: '100%',
                maxWidth: maxWidth + 'px',
                maxHeight: maxHeight + 'px',
                borderRadius: borderRadius + 'px',
                cursor: this.optionsSetup.imageClickable ? 'pointer' : 'default',
                transition: this.optionsSetup.imageHoverEffect ? 'transform 0.3s ease' : 'none',
                objectFit: displayMode,
                objectPosition: 'center'
            };
        }
    },

    watch: {
        value: {
            handler(val) {
                this.options = val;
                this.optionsSetup = val.setup;
                this.optionsPosition = val.position;
                this.optionsData = val.data;
                this.setOptionsData();
                // 清除之前的定时器并重新设置
                this.clearScrollTimer();
                this.$nextTick(() => {
                    this.startAutoScroll();
                });
            },
            deep: true
        },
        // 监听 optionsData 本身的变化（例如通过配置面板修改 staticData）
        optionsData: {
            handler() {
                // 当 optionsData 的内容变化时，重新设置数据并重启滚动
                this.setOptionsData();
                this.clearScrollTimer();
                this.$nextTick(() => {
                    this.startAutoScroll();
                });
            },
            deep: true
        },
        // 添加对静态数据的深度监听
        'optionsData.staticData': {
            handler(newVal) {
                // 当静态数据变化时，强制更新timelineData
                if (this.optionsData && this.optionsData.dataType === "staticData") {
                    this.setOptionsData();
                    this.clearScrollTimer();
                    this.$nextTick(() => {
                        this.startAutoScroll();
                    });
                }
            },
            deep: true
        }
    },

    mounted() {
        this.options = this.value;
        this.optionsSetup = this.value.setup;
        this.optionsPosition = this.value.position;
        this.optionsData = this.value.data;
        targetWidgetLinkageLogic(this);
        this.setOptionsData();
        // 启动自动滚动
        this.$nextTick(() => {
            this.startAutoScroll();
        });
    },
    beforeDestroy() {
        // 清理定时器
        this.clearScrollTimer();

        // 清理图片加载事件监听器
        if (this.$refs.timelineContent) {
            const images = this.$refs.timelineContent.querySelectorAll('img');
            images.forEach(img => {
                // 移除可能添加的事件监听器（使用实例保存的引用）
                if (this._imageLoadHandler) {
                    img.removeEventListener('load', this._imageLoadHandler);
                    img.removeEventListener('error', this._imageLoadHandler);
                }
            });
        }

        // 清理数据刷新定时器
        if (this.flagInter) {
            clearInterval(this.flagInter);
            this.flagInter = null;
        }
    },
    methods: {
        setOptionsData(e, paramsConfig) {
            const optionsData = this.optionsData;
            optionsData.dynamicData = optionsData.dynamicData || {};
            const myDynamicData = optionsData.dynamicData;

            clearInterval(this.flagInter);

            if (e && optionsData.dataType !== "staticData" && Object.keys(myDynamicData.contextData).length) {
                const keyArr = Object.keys(myDynamicData.contextData);
                paramsConfig.forEach((conf) => {
                    if (keyArr.includes(conf.targetKey)) {
                        myDynamicData.contextData[conf.targetKey] = e[conf.originKey];
                    }
                });
            }

            if (optionsData.dataType === "staticData") {
                // 深拷贝静态数据以确保 Vue 能检测到变化并避免共享引用问题
                try {
                    const arr = optionsData.staticData || [];
                    // 使用JSON序列化/反序列化进行深拷贝，确保完全独立的新对象
                    this.timelineData = JSON.parse(JSON.stringify(arr));
                } catch (err) {
                    // 如果JSON操作失败，回退到原来的实现
                    try {
                        const arr = optionsData.staticData || [];
                        this.timelineData = arr.map(item => Object.assign({}, item));
                    } catch (err2) {
                        this.timelineData = optionsData.staticData || [];
                    }
                }

                // 强制触发Vue更新
                this.$forceUpdate();

                // 静态数据时确保没有动态刷新的定时器
                if (this.flagInter) {
                    clearInterval(this.flagInter);
                    this.flagInter = null;
                }

                // 重新计算并启动滚动（异步以等待 DOM 更新）
                this.$nextTick(() => {
                    this.clearScrollTimer();
                    this.startAutoScroll();
                });
            } else if (optionsData.dataType === "dynamicData") {
                if (!myDynamicData.contextData) {
                    this.getData();
                    this.flagInter = setInterval(() => {
                        this.getData();
                    }, optionsData.refreshTime);
                }
            }
        },

        getData() {
            if (this.optionsData.dataType === "dynamicData") {
                const dynamicData = this.optionsData.dynamicData;
                this.timelineData = dynamicData.contextData || [];
            }
        },

        handleItemClick(item, index) {
            if (!this.optionsSetup.clickable) return;

            const clickData = {
                index: index,
                time: item.time,
                title: item.title,
                description: item.description,
                type: item.type || 'primary',
                ...item
            };

            originWidgetLinkageLogic(this, true, {
                currentData: clickData,
            });
        },
        // 处理图片点击事件
        handleImageClick(item, index) {
            if (!this.optionsSetup.imageClickable) return;

            // 阻止事件冒泡，避免触发item的点击事件
            event.stopPropagation();

            // 可以在这里实现图片放大查看的逻辑，目前只是打印日志
            console.log('Image clicked:', item.imageUrl);

            // 触发组件联动逻辑
            originWidgetLinkageLogic(this, true, {
                currentData: {
                    index: index,
                    imageUrl: item.imageUrl,
                    title: item.title,
                    ...item
                },
                eventType: 'imageClick'
            });
        },
        // 添加方法
        getDotStyle(itemType) {
            const size = this.optionsSetup.dotSize || 12;
            const primaryColor = this.optionsSetup.primaryColor || '#007bff';

            // 定义颜色映射
            const colorMap = {
                primary: '#007bff',
                success: '#28a745',
                info: '#17a2b8',
                warning: '#ffc107',
                error: '#dc3545'
            };

            // 使用项目类型对应的颜色，如果没有则使用主色调
            const dotColor = itemType && colorMap[itemType] ? colorMap[itemType] : primaryColor;

            // 根据是否空心调整样式
            if (this.optionsSetup.dotIsHollow) {
                return {
                    width: size + 'px',
                    height: size + 'px',
                    backgroundColor: 'transparent',
                    border: `2px solid ${dotColor}`,
                    display: 'flex',
                    alignItems: 'center',
                    justifyContent: 'center'
                };
            } else {
                return {
                    width: size + 'px',
                    height: size + 'px',
                    backgroundColor: dotColor,
                    border: `2px solid ${dotColor}`,
                    display: 'flex',
                    alignItems: 'center',
                    justifyContent: 'center'
                };
            }
        },
        // 启动自动滚动（入口）——委托到基于高度的计算方法
        startAutoScroll() {
            // 统一使用基于像素的滚动实现以避免百分比带来的抖动
            this.clearScrollTimer();
            this.startAutoScroll_calcHeight();
        },

        // 启动自动滚动,计算滚动高度
        startAutoScroll_calcHeight() {
            // 检查是否启用了滚动功能
            if (!this.optionsSetup.enableAutoScroll) {
                return;
            }

            // 只有垂直方向且内容超出容器高度时才启用自动滚动
            if (this.optionsSetup.direction !== 'vertical') {
                return;
            }

            // 确保timelineContent引用存在
            if (!this.$refs.timelineContent) {
                console.warn('timelineContent ref not found');
                return;
            }

            const containerHeight = this.optionsPosition.height;

            // 检查是否包含图片
            const images = this.$refs.timelineContent.querySelectorAll('img');
            const hasImages = images.length > 0;

            // 如果有图片，等待所有图片加载完成后再计算高度
            if (hasImages) {
                // 先尝试初始计算
                const initialHeight = this.calculateContentHeight();

                // 检查是否所有图片都已加载完成
                let loadedImages = 0;
                let allImagesLoaded = true;

                images.forEach(img => {
                    if (img.complete) {
                        loadedImages++;
                    } else {
                        allImagesLoaded = false;
                    }
                });

                // 如果所有图片已加载，直接使用初始计算
                if (allImagesLoaded) {
                    this.setupScrolling(containerHeight, initialHeight);
                    return;
                }

                // 否则，等待所有图片加载完成
                let imageLoadTimeout;
                const handleImageLoad = () => {
                    loadedImages++;

                    // 当所有图片加载完成后重新计算高度
                    if (loadedImages === images.length) {
                        // 清除超时定时器
                        if (imageLoadTimeout) {
                            clearTimeout(imageLoadTimeout);
                            imageLoadTimeout = null;
                        }

                        // 移除所有监听器并清理实例引用
                        images.forEach(img => {
                            img.removeEventListener('load', handleImageLoad);
                            img.removeEventListener('error', handleImageLoad);
                        });
                        this._imageLoadHandler = null;

                        // 给一点时间让布局稳定
                        setTimeout(() => {
                            const finalHeight = this.calculateContentHeight();
                            this.setupScrolling(containerHeight, finalHeight);
                        }, 100);
                    }
                };

                // 为每个未加载的图片添加加载事件监听
                // 保存引用以便后续清理
                this._imageLoadHandler = handleImageLoad;
                images.forEach(img => {
                    if (!img.complete) {
                        img.addEventListener('load', handleImageLoad);
                        img.addEventListener('error', handleImageLoad); // 错误情况下也继续
                    }
                });

                // 设置超时处理
                imageLoadTimeout = setTimeout(() => {
                    console.warn('Image loading timeout, using initial height calculation');
                    // 清理事件监听器
                    images.forEach(img => {
                        img.removeEventListener('load', handleImageLoad);
                        img.removeEventListener('error', handleImageLoad);
                    });
                    this._imageLoadHandler = null;
                    this.setupScrolling(containerHeight, initialHeight);
                }, 5000); // 5秒超时

                return;
            }

            // 没有图片时直接计算和设置滚动
            const contentHeight = this.calculateContentHeight();
            this.setupScrolling(containerHeight, contentHeight);
        },

        // 计算内容高度的专用方法
        calculateContentHeight() {
            if (!this.$refs.timelineContent) {
                return 0;
            }

            // 临时禁用过渡效果以准确测量高度
            this.$refs.timelineContent.style.transition = 'none';

            // 使用getBoundingClientRect()获取更准确的高度
            const contentRect = this.$refs.timelineContent.getBoundingClientRect();
            let contentHeight = contentRect.height;

            // 备用计算方式：使用scrollHeight作为后备
            const scrollHeight = this.$refs.timelineContent.scrollHeight;
            // 如果getBoundingClientRect获取的高度不可靠，使用scrollHeight
            if (isNaN(contentHeight) || contentHeight <= 0) {
                contentHeight = scrollHeight;
            }

            // 第三重保障：估算高度（通过计算每个项目的平均高度和项目数量）
            const items = this.$refs.timelineContent.querySelectorAll('.timeline-item');
            if (items.length > 0 && (isNaN(contentHeight) || contentHeight <= 0 || contentHeight < scrollHeight * 0.9)) {
                // 计算可见项目的平均高度
                let totalItemHeight = 0;
                const visibleItems = Math.min(items.length, 5); // 取前5个项目计算平均高度
                for (let i = 0; i < visibleItems; i++) {
                    const itemRect = items[i].getBoundingClientRect();
                    totalItemHeight += itemRect.height;
                }
                const avgItemHeight = totalItemHeight / visibleItems;
                // 估算总高度
                const estimatedHeight = avgItemHeight * items.length;
                // 如果估算高度更接近scrollHeight或更大，则使用估算高度
                if (estimatedHeight > contentHeight) {
                    contentHeight = Math.max(estimatedHeight, scrollHeight);
                }
            }

            // 恢复过渡效果
            setTimeout(() => {
                if (this.$refs.timelineContent) {
                    this.$refs.timelineContent.style.transition = 'transform 0.3s ease-out';
                }
            }, 0);

            return contentHeight;
        },

        setupScrolling(containerHeight, contentHeight) {
            // 检查内容是否超出容器高度
            if (contentHeight <= containerHeight || isNaN(contentHeight) || contentHeight <= 0) {
                return;
            }
            // 使用 requestAnimationFrame 实现更平滑、GPU 加速的滚动，避免频繁修改 transition
            // 在开始新滚动前清理可能存在的旧定时器/RAF，防止多重循环导致的高度不一致
            const previousPosition = this.scrollPosition || 0;
            this.clearScrollTimer();

            this.isScrolling = true;
            const scrollSpeedPerSecond = (this.optionsSetup && this.optionsSetup.scrollSpeed) ? Number(this.optionsSetup.scrollSpeed) : 20; // 像素/秒
            const loopPauseMs = (this.optionsSetup && typeof this.optionsSetup.loopPause !== 'undefined') ? Number(this.optionsSetup.loopPause) : 0; // ms，0 表示不暂停直接环回

            // 重新测量以获取更可靠的高度（处理图片加载/渲染差异）
            let measuredContentHeight = contentHeight;
            try {
                if (this.$refs.timelineContent) {
                    const rectH = this.$refs.timelineContent.getBoundingClientRect().height || 0;
                    const scrollH = this.$refs.timelineContent.scrollHeight || 0;
                    measuredContentHeight = Math.max(measuredContentHeight || 0, rectH, scrollH);
                }
            } catch (err) {
                // ignore
            }

            // 计算原始内容高度（用于循环长度），并进行取整处理
            const originalHeight = Math.max(0, Math.ceil(measuredContentHeight));

            // 如果尚未克隆（为了实现无缝循环，需要将内容复制一份），则复制子节点
            try {
                const ref = this.$refs.timelineContent;

                if (ref && ref.dataset && ref.dataset.cloned !== 'true') {
                    // 记录原始子项数量
                    const originalCount = ref.children.length;
                    this._originalItemCount = originalCount;

                    // 精确计算原始内容高度：逐项累加高度并包含 margin
                    let totalHeight = 0;
                    for (let i = 0; i < originalCount; i++) {
                        const el = ref.children[i];
                        if (!el) continue;
                        const rect = el.getBoundingClientRect();
                        let itemHeight = rect.height || el.offsetHeight || 0;
                        try {
                            const style = window.getComputedStyle(el);
                            const mt = parseFloat(style.marginTop) || 0;
                            const mb = parseFloat(style.marginBottom) || 0;
                            itemHeight += mt + mb;
                        } catch (e) {
                            // ignore getComputedStyle errors in SSR or tests
                        }
                        totalHeight += itemHeight;
                    }

                    // 包含容器的 padding/border，避免因容器内边距导致基准被低估
                    try {
                        const wrapperStyle = window.getComputedStyle(ref);
                        const paddingTop = parseFloat(wrapperStyle.paddingTop) || 0;
                        const paddingBottom = parseFloat(wrapperStyle.paddingBottom) || 0;
                        const borderTop = parseFloat(wrapperStyle.borderTopWidth) || 0;
                        const borderBottom = parseFloat(wrapperStyle.borderBottomWidth) || 0;
                        totalHeight += paddingTop + paddingBottom + borderTop + borderBottom;
                    } catch (e) {
                        // ignore in non-browser environments
                    }

                    // 作为后备，使用 scrollHeight 或 measuredContentHeight 中的最大值，确保基准不会小于实际渲染高度
                    const fallback = Math.max(ref.scrollHeight || 0, measuredContentHeight || 0);
                    const baseCandidate = Math.max(Math.ceil(totalHeight || 0), Math.ceil(fallback || 0));
                    this._originalContentHeight = baseCandidate;

                    // 复制当前子节点以实现连续循环（避免在复制过程中读取到新增节点）
                    const children = Array.from(ref.children).slice(0, originalCount);
                    children.forEach(child => {
                        const clone = child.cloneNode(true);
                        ref.appendChild(clone);
                    });
                    ref.dataset.cloned = 'true';
                } else if (ref) {
                    // 如果已经克隆，确保 originalContentHeight 至少有个后备值
                    // 修复：每次重新计算时都重新测量原始内容高度，以应对图片尺寸变化
                    let totalHeight = 0;
                    const originalCount = this._originalItemCount || (ref.children.length / 2);
                    for (let i = 0; i < originalCount; i++) {
                        const el = ref.children[i];
                        if (!el) continue;
                        const rect = el.getBoundingClientRect();
                        let itemHeight = rect.height || el.offsetHeight || 0;
                        try {
                            const style = window.getComputedStyle(el);
                            const mt = parseFloat(style.marginTop) || 0;
                            const mb = parseFloat(style.marginBottom) || 0;
                            itemHeight += mt + mb;
                        } catch (e) {
                            // ignore getComputedStyle errors in SSR or tests
                        }
                        totalHeight += itemHeight;
                    }

                    // 包含容器的 padding/border
                    try {
                        const wrapperStyle = window.getComputedStyle(ref);
                        const paddingTop = parseFloat(wrapperStyle.paddingTop) || 0;
                        const paddingBottom = parseFloat(wrapperStyle.paddingBottom) || 0;
                        const borderTop = parseFloat(wrapperStyle.borderTopWidth) || 0;
                        const borderBottom = parseFloat(wrapperStyle.borderBottomWidth) || 0;
                        totalHeight += paddingTop + paddingBottom + borderTop + borderBottom;
                    } catch (e) {
                        // ignore in non-browser environments
                    }

                    const fallback = Math.max(ref.scrollHeight || 0, measuredContentHeight || 0);
                    const baseCandidate = Math.max(Math.ceil(totalHeight || 0), Math.ceil(fallback || 0));
                    this._originalContentHeight = baseCandidate;
                } else {
                    this._originalContentHeight = measuredContentHeight || 0;
                }
            } catch (err) {
                // ignore cloning or measurement errors
                this._originalContentHeight = this._originalContentHeight || measuredContentHeight || 0;
            }

            // 使用克隆前测得的原始内容高度作为一次循环的距离（maxScroll）
            const base = (this._originalContentHeight && this._originalContentHeight > 0) ? Math.ceil(this._originalContentHeight) : originalHeight;
            const maxScroll = Math.max(0, base - Math.floor(containerHeight));
            let isAtBottom = false;

            // 保证初始位置不超过新的最大滚动值
            this.scrollPosition = Math.min(previousPosition, Math.max(0, maxScroll));

            if (this.$refs.timelineContent) {
                this.$refs.timelineContent.style.willChange = 'transform';
                // 使用无过渡的即时位移，避免 CSS transition 与手动更新冲突
                this.$refs.timelineContent.style.transition = 'transform 0s linear';
                this.$refs.timelineContent.style.transform = `translate3d(0, -${this.scrollPosition}px, 0)`;
            }

            const step = (timestamp) => {
                // 确保组件仍然存在
                if (!this.$refs.timelineContent || !this.isScrolling) {
                    this.clearScrollTimer();
                    return;
                }

                if (!this.lastTimestamp) this.lastTimestamp = timestamp;
                const delta = timestamp - this.lastTimestamp;

                // 限制delta的最大值，防止页面失焦后回来出现大的跳跃
                const clampedDelta = Math.min(delta, 100);
                this.lastTimestamp = timestamp;

                if (isAtBottom) {
                    // 在底部等待，由 setTimeout 取消等待
                    this.rafId = requestAnimationFrame(step);
                    return;
                }

                // 计算将要增加的位置（像素）
                const increment = (scrollSpeedPerSecond * clampedDelta) / 1000;
                const loopBase = (base || originalHeight) || 1; // 一个完整内容的高度

                // 计算下一个位置
                let nextPosition = this.scrollPosition + increment;

                // 修复循环不完整就重新循环的问题
                // 改进循环检测逻辑：只有当滚动位置超过内容高度减去容器高度时才触发循环
                const actualMaxScroll = loopBase - Math.floor(containerHeight);

                // 检测是否需要环回（无过渡，避免可见跳变）
                if (nextPosition >= loopBase) {
                    // 使用取模实现平滑环回，并把结果写回 scrollPosition
                    this.scrollPosition = nextPosition % loopBase;
                }
                // 检测是否在本帧跨越视觉末尾（actualMaxScroll）并需要暂停
                else if (this.scrollPosition < actualMaxScroll && nextPosition >= actualMaxScroll && loopPauseMs > 0) {
                    // 精确推进到视觉末尾位置并暂停，等待超时再继续
                    this.scrollPosition = actualMaxScroll;
                    isAtBottom = true;
                    if (this._wrapTimeout) clearTimeout(this._wrapTimeout);
                    this._wrapTimeout = setTimeout(() => {
                        isAtBottom = false;
                        this._wrapTimeout = null;
                        // 重置时间基准以避免下一帧产生大 delta
                        this.lastTimestamp = performance.now();
                    }, loopPauseMs);
                } else {
                    // 正常推进
                    this.scrollPosition = nextPosition;
                }

                // 应用像素位移
                const applied = Math.round(this.scrollPosition);
                this.$refs.timelineContent.style.transform = `translate3d(0, -${applied}px, 0)`;
                this.rafId = requestAnimationFrame(step);
            };

            // 启动 RAF 循环
            this.lastTimestamp = performance.now(); // 使用performance.now()获取更精确的时间
            this.rafId = requestAnimationFrame(step);
        },
        // 清除滚动定时器 / RAF
        clearScrollTimer() {
            // 清除旧的 setInterval（如果存在）
            if (this.scrollTimer) {
                clearInterval(this.scrollTimer);
                this.scrollTimer = null;
            }

            // 取消 requestAnimationFrame 循环
            if (this.rafId) {
                cancelAnimationFrame(this.rafId);
                this.rafId = null;
            }

            this.lastTimestamp = null;
            this.scrollPosition = 0;
            this.isScrolling = false;
            // 清理 wrap timeout（如果有）
            if (this._wrapTimeout) {
                clearTimeout(this._wrapTimeout);
                this._wrapTimeout = null;
            }

            if (this.$refs.timelineContent) {
                // 如果之前克隆了节点，用 _originalItemCount 来移除多余的克隆
                try {
                    const ref = this.$refs.timelineContent;
                    if (ref.dataset && ref.dataset.cloned === 'true' && this._originalItemCount > 0) {
                        const total = ref.children.length;
                        // 移除最后 this._originalItemCount 个子节点
                        for (let i = 0; i < this._originalItemCount; i++) {
                            const idx = ref.children.length - 1;
                            if (idx >= 0) ref.removeChild(ref.children[idx]);
                        }
                        delete ref.dataset.cloned;
                        this._originalItemCount = 0;
                    }
                } catch (err) {
                    // ignore
                }

                // 移除图片加载监听器（如果存在）
                try {
                    const imgs = this.$refs.timelineContent.querySelectorAll('img');
                    if (this._imageLoadHandler) {
                        imgs.forEach(img => {
                            img.removeEventListener('load', this._imageLoadHandler);
                            img.removeEventListener('error', this._imageLoadHandler);
                        });
                        this._imageLoadHandler = null;
                    }
                } catch (err) {
                    // ignore
                }

                // 恢复过渡效果为默认并重置位置
                this.$refs.timelineContent.style.transition = 'transform 0.3s ease-out';
                this.$refs.timelineContent.style.transform = 'translate3d(0, 0, 0)';
                this.$refs.timelineContent.style.willChange = '';
            }
        },
        // 添加图片加载完成后的刷新方法，可在需要时手动调用
        refreshScrollHeight() {
            this.clearScrollTimer();
            this.$nextTick(() => {
                this.startAutoScroll();
            });
        },
        // （已合并）


    }
};
</script>

<style lang="scss" scoped>
.timeline-nuxt-container {
    width: 100%;
    height: 100%;
    overflow: hidden; // 隐藏滚动条
}

.timeline-nuxt {
    position: relative;
    padding: 20px 0;
    transition: transform 0.3s ease-out; // 平滑滚动
}

/* 垂直布局 */
.timeline-vertical {
    .timeline-item {
        position: relative;
        padding-left: 40px;
        padding-right: 20px;
        min-height: 80px; // 确保有足够的空间显示内容

        &:last-child {
            margin-bottom: 0 !important;
        }
    }

    .timeline-dot {
        position: absolute;
        left: 0;
        top: 8px; // 调整圆点位置，与内容对齐
        border-radius: 50%;
        display: flex;
        align-items: center;
        justify-content: center;
        z-index: 2;

        &-inner {
            background-color: #ffffff;
            border-radius: 50%;
        }
    }

    .timeline-line {
        position: absolute;
        left: calc((var(--dot-size, 12px) / 2) - 1px); // 使用CSS变量动态计算
        top: 24px; // 从圆点底部开始
        bottom: 8px; // 到下一个项目的顶部
        width: 2px;
        z-index: 1;
        background-color: var(--line-color, #007bff); // 使用CSS变量
    }

    .timeline-item:last-child .timeline-line {
        display: none;
    }
}

/* 水平布局 */
.timeline-horizontal {
    display: flex;
    overflow-x: auto;
    padding: 20px;
    align-items: flex-start; // 确保项目顶部对齐

    .timeline-item {
        position: relative;
        padding-top: 40px;
        min-width: 200px;
        flex-shrink: 0;
        margin-bottom: 0; // 重置垂直间距
        text-align: center; // 水平布局默认居中对齐


        &:last-child {
            margin-right: 0 !important;
        }
    }

    .timeline-dot {
        position: absolute;
        top: 0;
        left: 50%;
        transform: translateX(-50%);
        border-radius: 50%;
        display: flex;
        align-items: center;
        justify-content: center;
        z-index: 2;

        &-inner {
            background-color: #ffffff;
            border-radius: 50%;
        }
    }

    .timeline-line {
        position: absolute;
        top: calc((var(--dot-size, 12px) / 2) - 1px);
        left: 50%;
        right: -50%;
        height: 2px;
        z-index: 1;
        background-color: var(--line-color, #007bff);
    }

    .timeline-item:last-child .timeline-line {
        display: none;
    }
}

/* 对齐方式支持 */
.timeline-content {
    text-align: left;
    /* 默认左对齐 */
}

/* 卡片类型 */
.timeline-type-card {
    .timeline-content {
        transition: all 0.3s ease;

        &:hover {
            transform: translateY(-2px);
            box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15) !important;
        }
    }
}

/* 简约类型 */
.timeline-type-minimal {
    .timeline-content {
        padding: 8px 0;
    }

    .timeline-time {
        margin-bottom: 2px;
    }

    .timeline-title {
        margin-bottom: 0;
    }
}

/* 交互效果 */
.timeline-item-clickable {
    cursor: pointer;
}

.timeline-item-animated {
    animation: fadeInUp 0.6s ease forwards;
}

@keyframes fadeInUp {
    from {
        opacity: 0;
        transform: translateY(20px);
    }

    to {
        opacity: 1;
        transform: translateY(0);
    }
}

/* 不同类型圆点颜色 */
.timeline-dot-primary {
    background-color: #007bff;
    border-color: #007bff;
}

.timeline-dot-success {
    background-color: #28a745;
    border-color: #28a745;
}

.timeline-dot-info {
    background-color: #17a2b8;
    border-color: #17a2b8;
}

.timeline-dot-warning {
    background-color: #ffc107;
    border-color: #ffc107;
}

.timeline-dot-error {
    background-color: #dc3545;
    border-color: #dc3545;
}

// 空心圆点特殊样式
:deep(.timeline-dot[style*="background-color: transparent"]) {
    background-color: transparent !important;

    .timeline-dot-inner {
        display: none;
    }
}

/* 图片相关样式 */
.timeline-image-container {
    overflow: hidden;

    &:hover .timeline-image {
        transform: scale(1.02);
    }

    &:hover .timeline-image-hint {
        opacity: 1;
    }
}

.timeline-image {
    display: block;
    // 移除原有的object-fit: contain，让JS计算属性控制
    border: 1px solid #e5e7eb;
}

.timeline-image-hint {
    position: absolute;
    bottom: 8px;
    right: 8px;
    background-color: rgba(0, 0, 0, 0.6);
    color: white;
    padding: 2px 8px;
    border-radius: 4px;
    font-size: 12px;
    opacity: 0;
    transition: opacity 0.3s ease;
}

/* 响应式设计 */
@media (max-width: 768px) {
    .timeline-horizontal {
        .timeline-item {
            min-width: 150px;
        }
    }

    .timeline-vertical {
        .timeline-item {
            padding-left: 30px;
        }
    }

    .timeline-image {
        max-width: 100% !important;
    }
}
</style>